#include <stdio.h>
#include <stdlib.h>


#include <string.h>
#include <strings.h>
#include "include/gestiondetypes.h"
#include "include/gestiondesmessages.h"


int EstGrosBoutiste()   // renvoie 1 si le système traite les poids forts en premier
{                       // source http://www.ibm.com/developerworks/aix/library/au-endianc/index.html?ca=drs-
    const int i = 1;
    char *ptr = (char *)&i;

    if (ptr[0] == 1)
        return 0;
    else
        return 1;
}

short inversesuitede4char (char *cha)   // permet d'inverser un tableau de 4 char
{                                       // source http://www.ibm.com/developerworks/aix/library/au-endianc/index.html?ca=drs-
    int i;
    char *ptr = (char *)&i;

    if (EstGrosBoutiste())
    {
        ptr[0] = cha[0];
        ptr[1] = cha[1];
        ptr[2] = cha[2];
        ptr[3] = cha[3];
    }
    else
    {
        ptr[0] = cha[3];
        ptr[1] = cha[2];
        ptr[2] = cha[1];
        ptr[3] = cha[0];
    }
    return i;
}


LISTE creer_liste(void)
{
    return NULL;	// une liste vide est un pointeur nul
}

int est_vide(const LISTE L)
{
    return !L;
}

//doit avoir precedement : ELEMENT_LEXEME* pelementdelaliste=calloc(1,sizeof(ELEMENT_LEXEME);
LISTE ajout_tete(void * pelementdelaliste,LISTE tete)
{
    if (pelementdelaliste==NULL)
    {
        perror("Echec d\'allocation dynamique.");
        ecriremessage(NIVEAU_ERROR, "Impossible d\'ajouter un element en tete de la liste : Echec d\'allocation dynamique.");
        exit(EXIT_FAILURE);
    }

    LISTE p;
    p=(LISTE) calloc(1,sizeof(*p));

    if (p==NULL)
    {
        perror("Echec d\'allocation dynamique.");
        ecriremessage(NIVEAU_ERROR, "Impossible d\'ajouter un element en tete de la liste : Echec d\'allocation dynamique.");
        exit(EXIT_FAILURE);
    }

    p->element=pelementdelaliste;		// la copie marche avec les structures
    p->suivant=tete;	// donc ELEMENT peut être une structure.
    return p;		    // (la copie est la seule action globale utilisable)
}

LISTE supprimer_tete(LISTE L)
{
    if (est_vide(L)) return NULL;
    else
    {
        LISTE p;
        p=L->suivant;
        free(L);    // ! ne free pas la chaine allouee dynamiquement
        return p;
    }
}

LISTE supprimer_element_lexeme_tete(LISTE L)
{
    if (est_vide(L))
    {
        return NULL;
    }
    // L->element n'existe parfois pas
    if ( ((ELEMENT_LEXEME*)(L->element))->chaine_lexeme )
    {
        free( ((ELEMENT_LEXEME*)(L->element))->chaine_lexeme );
    }
    free(L->element);
    return L->suivant;
}

void liberer_lexeme_solitaire(ELEMENT_LEXEME *e)
{
    free(e->chaine_lexeme);
    free(e);
}


//doit avoir precedement : ELEMENT_LEXEME* pelementdelaliste=calloc(1,sizeof(ELEMENT_LEXEME);
LISTE ajout_queue(void* pelement,LISTE L)
{
    if (pelement==NULL)
    {
        perror("Echec d\'allocation dynamique.");
        ecriremessage(NIVEAU_ERROR, "Impossible d\'ajouter un element en queue de la liste : Echec d\'allocation dynamique.");
        exit(EXIT_FAILURE);
    }
    LISTE p;
    LISTE i=L;

    p=calloc(1, sizeof(*p));
    if (p==NULL)
    {
        perror("Echec d\'allocation dynamique.");
        ecriremessage(NIVEAU_ERROR, "Impossible d\'ajouter un element en queue de la liste : Echec d\'allocation dynamique.");
        exit(EXIT_FAILURE);
    }

    p->element=pelement;
    p->suivant=NULL;

    if (est_vide(L))
    {
        return p;
    }
    while (!est_vide(i->suivant))
    {
        i=i->suivant;	//on envoie i à la fin
    }
    i->suivant=p;
    return L;
}


void visualiser_liste_lexeme(LISTE liste)
{
    LISTE L = liste; // L est le curseur d'une cellule
    while(L!=NULL)//tant que la liste n'est pas vide
    {
        if ( ((ELEMENT_LEXEME*)(L->element))->type_de_lexeme != TYPE_Nouvelle_ligne )
        {
            ecriremessage(NIVEAU_DEBUG,"\tLigne %03d%32s << %16s >>\n",\
                          \
                          ((ELEMENT_LEXEME*)(L->element))->num_ligne_lexeme,\
                          traduire_type_lexeme(((ELEMENT_LEXEME*)(L->element))->type_de_lexeme),\
                          ((ELEMENT_LEXEME*)(L->element))->chaine_lexeme);
        }
        else
        {
            ecriremessage(NIVEAU_DEBUG,"\tLigne %03d%32s << %16s >>\n",\
                          \
                          ((ELEMENT_LEXEME*)(L->element))->num_ligne_lexeme,\
                          traduire_type_lexeme(((ELEMENT_LEXEME*)(L->element))->type_de_lexeme),\
                          "\\n" );
        }

        L=L->suivant;
    }
}

void initialiser(ELEMENT_LEXEME E)
{
    E.type_de_lexeme=0;
    E.chaine_lexeme=NULL;
}

char* traduire_type_lexeme(TYPE_DE_LEXEME type_a_traduire)
{
    // Attention il doit y avoir conformité avec les types de l'entête
    static char* chaine_traduction[] =
    {
        /// TRADUCTION            /// TYPE_DE_LEXEME
        "Argument de directive",//  0
        "Commentaire",          //  1
        "Directive",            //  2
        "Erreur de type",       //  3
        "Etiquette",            //  4
        "Minus",                //  5
        "Nouvelle ligne",       //  6
        "Operande",             //  7
        "Operateur",            //  8
        "Parenthese droite",    //  9
        "Parenthese gauche",    // 10
        "Plus",                 // 11
        "Registre",             // 12
        "Symbole",              // 13
        "Valeur decimale",      // 14
        "Valeur hexadecimale",  // 15
        "Valeur octale",        // 16
        "Valeur traduite",      // 17
        "Virgule"               // 18
    };

    if (type_a_traduire>=0 && type_a_traduire<=NOMBRE_TYPES)  // comparer au TYPE_DE_LEXEME min et max (cf source/include/gestiondetypes.h>typedef struct TYPE_DE_LEXEME)
        return chaine_traduction[type_a_traduire];
    ecriremessage( NIVEAU_ERROR , "Type de lexeme %d non traduit.\n",type_a_traduire);
    return NULL;
}

int max(int a, int b)
{
    if (a>b) return a;
    else return b;
}

int min(int a, int b)
{
    if (a<b) return a;
    else return b;
}

void copie_tete_dans_lexeme(LISTE tete,ELEMENT_LEXEME * plexeme)
//Copie la tete de liste dans le lexeme
{
    plexeme->chaine_lexeme=strdup(  ( ( ELEMENT_LEXEME* )(tete->element))->chaine_lexeme  );
    plexeme->num_ligne_lexeme = ( ( ELEMENT_LEXEME* )(tete->element))->num_ligne_lexeme;
    plexeme->type_de_lexeme = ( ( ELEMENT_LEXEME* )(tete->element))->type_de_lexeme;
}

void liberer_liste_lexemes(LISTE liste_a_supprimer)
{
    ecriremessage(NIVEAU_DEBUG, "Lib%cration d'une liste de lex%cmes...\n", E_ACCENTAIGU, E_ACCENTGRAVE);
    liberer_lexemes_liste(liste_a_supprimer);
    liberer_liste(liste_a_supprimer);
    ecriremessage(NIVEAU_DEBUG, "... Lib%cration de la liste de lex%cmes termin%ce.\n", E_ACCENTAIGU, E_ACCENTGRAVE, E_ACCENTAIGU);
    return;
}

void liberer_liste(LISTE liste_a_liberer)
{
    //ecriremessage(NIVEAU_DEBUG, "Liberation d\'une liste...\n");
    while(!est_vide(liste_a_liberer))
    {
        liste_a_liberer = supprimer_tete(liste_a_liberer);
    }
    //ecriremessage(NIVEAU_DEBUG, "...Liberation de la liste terminee.\n");
}

void liberer_lexemes_liste(LISTE liste_dont_les_lexemes_sont_a_liberer)
{
    //ecriremessage(NIVEAU_DEBUG, "Liberation des lexemes d'une liste...\n");

    while(!est_vide(liste_dont_les_lexemes_sont_a_liberer))
    {
        liste_dont_les_lexemes_sont_a_liberer = supprimer_element_lexeme_tete(liste_dont_les_lexemes_sont_a_liberer);
    }
    //ecriremessage(NIVEAU_DEBUG, "...Liberation des lexemes de la liste terminee.\n");
    return;
}

int visualiser_tableau_ligne_text(LIGNE_INSTRUCT_ASM *t_instruct,INSTRUCTION dico[])
{
    ecriremessage(NIVEAU_DEBUG, "\n");
    ecriremessage(NIVEAU_DEBUG, "Visualisation de la liste d'instructions :\n");
    ecriremessage(NIVEAU_DEBUG, "\n");
    int i=0;
    int erreur = 0;
    for(i=0; t_instruct[i].num_instruction !=-1 ; i++)
    {
        if (t_instruct[i].num_instruction>=-1)
        {
            ecriremessage(NIVEAU_DEBUG, "Instruction : <%s>, ligne d'origine : %3d\n"\
                          , dico[t_instruct[i].num_instruction].nom, t_instruct[i].num_ligne_source);
        }
        else
        {
            if (t_instruct[i].num_instruction==-2)
                ecriremessage(NIVEAU_DEBUG, "Instruction : SPECIALE <LUI>, ligne d'origine : %3d \n"\
                              , t_instruct[i].num_ligne_source);
            else
            {
                erreur++;   // precaution
                ecriremessage(NIVEAU_ERROR, "Instruction de numero <%d> invalide ! %d instruction(s) est (sont) invalide(s) dans le tableau.\n",t_instruct[i].num_instruction,erreur);
            }
        }

        if (!erreur)    // precaution
        {
            if (est_vide( t_instruct[i].tab_liste[0]) )
            {
                ecriremessage(NIVEAU_DEBUG, "Aucun argument %c cette instruction.\n", A_ACCENTGRAVE);
            }
            else
            {
                ecriremessage(NIVEAU_DEBUG, "Voici la liste des arguments de cette instruction :\n");
                int j;
                for(j=0 ; j<NOMBRE_MAX_D_OPERANDES ; j++)
                {
                    visualiser_liste_lexeme(t_instruct[i].tab_liste[j]);
                }
            }
        }


    }

    ecriremessage(NIVEAU_DEBUG, "Instruction : <%d> et fin du tableau. Sortie de la boucle for.\n", t_instruct[i].num_instruction);
    return i;
}

int liberer_tableau_ligne_text(LIGNE_INSTRUCT_ASM *t_instruct)
{
    ecriremessage(NIVEAU_DEBUG, "Lib%cration de la liste d'instructions %p...\n", E_ACCENTAIGU, *t_instruct);
    int i=0;
    int j;
    for(i=0; t_instruct[i].num_instruction !=-1 ; i++)
    {
        ecriremessage(NIVEAU_DEBUG, "\tLib%cration de l\'instruction d\'indice %d de num_inst=%d...\n",E_ACCENTAIGU, i,t_instruct[i].num_instruction);

        if (est_vide( t_instruct[i].tab_liste[0]) )
        {
            ecriremessage(NIVEAU_DEBUG, "\t\tAucun argument %c lib%crer sur cette instruction.\n", A_ACCENTGRAVE, E_ACCENTAIGU);
        }
        else
        {
            ecriremessage(NIVEAU_DEBUG, "\t\tLib%cration de la liste des arguments de cette instruction...\n", E_ACCENTAIGU);

            for(j=0 ; j<NOMBRE_MAX_D_OPERANDES ; j++)
            {
                liberer_liste_lexemes(t_instruct[i].tab_liste[j]);
            }
            ecriremessage(NIVEAU_DEBUG, "\t\t...Lib%cration des %d arguments de cette instruction terminee.\n",E_ACCENTAIGU, j);
        }
        ecriremessage(NIVEAU_DEBUG, "\t...Lib%cration de l\'instruction d\'indice %d termin%ce.\n",E_ACCENTAIGU, i, E_ACCENTAIGU);
    }

    free(t_instruct);
    ecriremessage(NIVEAU_DEBUG, "...Lib%cration de la liste termin%ce. %d instructions ont ete lib%cr%ces.\n",E_ACCENTAIGU, E_ACCENTAIGU, i, E_ACCENTAIGU, E_ACCENTAIGU);
    return i;
}



void visualiser_liste_etiquette(LISTE liste)
{
    LISTE L = liste; // L est le curseur d'une cellule
    while(L!=NULL)//tant que la liste n'est pas vide
    {
        //On affiche les données cellule par cellule

        if (((ELEMENT_ETIQUETTE*)(L->element))->type>0)
        {

            ecriremessage(NIVEAU_DEBUG,"\t.text : \t0x%X : \t<< %s >>.\n",\
                          \
                          ((ELEMENT_ETIQUETTE*)(L->element))->num_ligne*4,\
                          ((ELEMENT_ETIQUETTE*)(L->element))->nom );


        }
        if (((ELEMENT_ETIQUETTE*)(L->element))->type<0)
        {

            ecriremessage(NIVEAU_DEBUG,"\t.data : \t0x%X : \t<< %s >>.\n",\
                          \
                          ((ELEMENT_ETIQUETTE*)(L->element))->num_ligne*4,\
                          ((ELEMENT_ETIQUETTE*)(L->element))->nom );
        }
        L=L->suivant;
    }
}

int liberer_liste_directive(LIGNE_DIRECTIVE_ASM* liste_directive_a_liberer)
{

    ///    typedef struct
    //    {
    //        TYPE_DE_DIRECTIVE directive;
    //        LISTE *tab_liste;	//chaine de Liste
    //        long long int* chaine_valeur;
    //        int nombre_de_valeurs;
    //        int adresse_ligne;
    ///    } LIGNE_DIRECTIVE_ASM;


    int i=0;

    int k=0;
    ecriremessage(NIVEAU_DEBUG, "Lib%cration d\'une liste de directives...\n", E_ACCENTAIGU);

    if (liste_directive_a_liberer)
    {
        for(k=0; liste_directive_a_liberer[k].directive!=TYPE_FIN_DE_CHAINE; k++)
        {
            ecriremessage(NIVEAU_DEBUG, "\t lib%cration directive %03d\n", E_ACCENTAIGU, k);


            for(i=0; liste_directive_a_liberer[k].tab_liste[i]!=NULL; i++)
            {
                ecriremessage(NIVEAU_DEBUG, "\t\t lib%cration liste directive %02d %02d\n",E_ACCENTAIGU, k, i);

                liberer_liste_lexemes(liste_directive_a_liberer[k].tab_liste[i]);

                ecriremessage(NIVEAU_DEBUG, "\t\t lib%cration liste directive %02d %02d termin%ce\n",E_ACCENTAIGU, k, i, E_ACCENTAIGU);
            }

            if (liste_directive_a_liberer[k].chaine_valeur!=NULL)  //tableau_directives[i].chaine_valeur = calloc(tableau_directives[i].nombre_de_valeurs, sizeof(int));
            {
                free(liste_directive_a_liberer[k].chaine_valeur);
            }
            free(liste_directive_a_liberer[k].tab_liste);


            ecriremessage(NIVEAU_DEBUG, "\t lib%cration directive %03d termin%ce\n", E_ACCENTAIGU, k, E_ACCENTAIGU);
        }
        free(liste_directive_a_liberer);    // pas certain que cette ligne soit indispensable
    }
    ecriremessage(NIVEAU_DEBUG, "...  Lib%cration de la liste de directives termin%ce.\n", E_ACCENTAIGU, E_ACCENTAIGU);
    return i;
}


int visualiser_tableau_ligne_data(LIGNE_DIRECTIVE_ASM *t_direct)
{
    ecriremessage(NIVEAU_DEBUG, "Visualisation de la liste de directives:\n");
    int i=0;
    for(i=0; t_direct[i].directive>=0 ; i++)
    {
        switch(t_direct[i].directive)
        {
        case TYPE_BYTE :
            ecriremessage(NIVEAU_DEBUG, "Directive de numero %d de type << .byte >>.\n", t_direct[i].directive);
            break;
        case TYPE_WORD :
            ecriremessage(NIVEAU_DEBUG, "Directive de numero %d de type << .word >>.\n", t_direct[i].directive);
            break;
        case TYPE_SPACE :
            ecriremessage(NIVEAU_DEBUG, "Directive de numero %d de type << .space >>.\n", t_direct[i].directive);
            break;
        default :
            ecriremessage(NIVEAU_ERROR, "Numero de directive %d invalide.\n", t_direct[i].directive);
            break;
        }


        if (est_vide( t_direct[i].tab_liste[0]) )
        {
            ecriremessage(NIVEAU_DEBUG, "Aucun argument %c cette directive.\n", A_ACCENTGRAVE);
        }
        else
        {
            ecriremessage(NIVEAU_DEBUG, "Voici la liste des arguments de cette directive :\n");
            int j;
            for(j=0 ; !est_vide(t_direct[i].tab_liste[j]) && j<t_direct[i].nombre_de_valeurs ; j++)
            {
                visualiser_liste_lexeme(t_direct[i].tab_liste[j]);
            }
        }

    }

    return i;
}
void ** allouer_matrice(int taille_colonne, int taille_ligne, int taille_element)
{
    int i;

    void ** val_retour = calloc(taille_colonne,sizeof(void*));

    if (!val_retour)
    {
        return NULL;
    }

    for(i=0; i<taille_colonne; i++)
    {
        val_retour[i]=calloc(taille_ligne, taille_element);
        if (!val_retour[i])
        {
            return NULL;
        }
    }

    return val_retour;
}
//////////Test de allouer_matrice : le main ////////////////////
/*
 #define I 15
 #define J 10
 int main (int argc, const char * argv[])
 {
 int i,j;
 int **m;
 m=(int**)allouer_matrice(J,I,sizeof(int));

 for (i=0; i<I; i++) {
 for (j=0; j<J;j++) {
 m[j][i]=100*i+j;

 printf("%d\t",m[j][i]);
 }
 printf("\n");
 }

 return 0;
 }

 */


LISTE ajout_queue_liste_lexeme(LISTE tete, TYPE_DE_LEXEME type, char * chaine, unsigned int num_ligne)
{
//ecriremessage(NIVEAU_DEBUG,"Test 6--+-+++1 %d\n",sizeof(ELEMENT_LEXEME));
    ELEMENT_LEXEME* pelement = calloc(1,sizeof(*pelement));
//ecriremessage(NIVEAU_DEBUG,"Test 6--+-+++2 \n");
    LISTE Liste ;
    Liste = ajout_queue((void*)pelement, tete);
//ecriremessage(NIVEAU_DEBUG,"Test 6--+-+++3 \n");
    pelement->type_de_lexeme = type;
    pelement->num_ligne_lexeme = num_ligne;
//ecriremessage(NIVEAU_DEBUG,"Test 6--+-+++4 %s\n",chaine);

    if (chaine) pelement->chaine_lexeme = strdup(chaine);

    ecriremessage(NIVEAU_DEBUG,"\n");
    if (pelement->chaine_lexeme==NULL)
    {
        ecriremessage(NIVEAU_ERROR, "L\'allocation dynamique dans gestiondetypes.c:ajout_queue_liste_lexeme() a echoue. Je suis un programme triste. :-(\n");
        ecriremessage(NIVEAU_DEBUG, "banza%c\n", 139);  // BANZAI!!
        exit(EXIT_FAILURE);
    }

    return Liste;
}

//doit avoir precedement : ELEMENT_LEXEME* pelementdelaliste=calloc(1,sizeof(ELEMENT_LEXEME);
//LISTE ajout_queue(void* pelement,LISTE L)
